/**
 * tree - jQuery xui
 *
 * Licensed under the GPL:
 *   http://www.gnu.org/licenses/gpl.txt
 *
 * Copyright 2013 xjb [ beymy.en@gmail.com ]
 *
 */
(function($) {
    /**
     * 从<ul>构建树
     */
    function buildTree(target, ul, data) {
        if (target == ul) { //从根加载
            //清空树
            $(target).empty();
            $(target).addClass('tree');
            //清除以前的内部数据
            $.removeData(target, 'tree.data');
            $.data(target, 'tree.data', {});
            //重置uid
            $.data(target, 'tree').uid = 0;
        }

        var state = $.data(target, 'tree');
        var opts = state.options;
        var uid = state.uid;
        var treeData = $.data(target, 'tree.data');

        function buildNodes(ul, children, depth) {
            for (var i = 0; i < children.length; i++) {
                var id = 'N' + uid++; //内部结点ID
                var item = children[i];
                var iconCls = '';
                if (item.iconCls) iconCls = ' ' + item.iconCls;

                // 结点只有open和closed两个状态
                if (item.state != 'open' && item.state != 'closed') {
                    item.state = 'open';
                }

                ul.push('<li>');
                //结点body
                ul.push('<div class="tree-node" node-id="' + id + '">');
                //缩进
                for (var j = 0; j < depth; j++) {
                    ul.push('<span class="tree-indent"></span>');
                }
                if (item.children || item.state == 'closed') {
                    if (item.state == 'open') {
                        ul.push('<span class="tree-hit tree-expanded"></span>');
                        ul.push('<span class="tree-folder tree-folder-open' + iconCls + '"></span>');
                    } else {
                        ul.push('<span class="tree-hit tree-collapsed"></span>');
                        ul.push('<span class="tree-folder' + iconCls + '"></span>');
                    }
                } else {
                    ul.push('<span class="tree-indent"></span>');
                    ul.push('<span class="tree-file' + iconCls + '"></span>');
                }
                //checkbox
                if (opts.checkbox) {
                    if (item.checked) {
                        ul.push('<span class="tree-checkbox tree-checkbox1"></span>');
                    } else {
                        ul.push('<span class="tree-checkbox tree-checkbox0"></span>');
                    }
                }
                ul.push('<span class="tree-title">' + item.text + ' </span>');
                ul.push('</div>');

                //存储结点数据
                treeData[id] = {
                    id: item.id,
                    text: item.text,
                    attributes: item.attributes
                };
                //子结点
                if (item.children) {
                    if (item.state == 'open') {
                        ul.push('<ul>');
                    } else {
                        ul.push('<ul style="display: none;">');
                    }
                    buildNodes(ul, item.children, depth + 1);
                    ul.push('</ul>');
                }
                ul.push('</li>');
            }
        }
        var depth = $(ul).prev().find('>span.tree-indent,>span.tree-hit').length;
        var html = [];
        buildNodes(html, data, depth);
        $(target).html(html.join(''));
    }

    function expandNode(target, node) {
        var opts = $.data(target, 'tree').options;
        var hit = $('>span.tree-hit', node);
        if (hit.length == 0) return; // is a leaf node
        if (hit.hasClass('tree-collapsed')) {
            hit.removeClass('tree-collapsed tree-collapsed-hover').addClass('tree-expanded');
            hit.next().addClass('tree-folder-open');
            var ul = $(node).next();
            if (ul.length) {
                if (opts.animate) {
                    ul.slideDown();
                } else {
                    ul.css('display', 'block');
                }
            } else {
                var id = $.data(target, 'tree.data')[$(node).attr('node-id')].id;
                var subul = $('<ul></ul>').insertAfter(node);
                request(target, subul, {
                    id: id
                }); // 请求子结点数据
            }
        }
    }

    function expandAllNode(target, node) {
        expandNode(target, node);
        $(node).next().find("div.tree-node").each(function() {
            expandNode(target, this);
        });
    }

    function collapseNode(target, node) {
        var opts = $.data(target, 'tree').options;
        var hit = $('>span.tree-hit', node);
        if (hit.length == 0) return; // 叶子结点
        if (hit.hasClass('tree-expanded')) {
            hit.removeClass('tree-expanded tree-expanded-hover').addClass('tree-collapsed');
            hit.next().removeClass('tree-folder-open');
            if (opts.animate) {
                $(node).next().slideUp();
            } else {
                $(node).next().css('display', 'none');
            }
        }
    }

    function collapseAllNode(target, node) {
        collapseNode(target, node);
        $(node).next().find("div.tree-node").each(function() {
            collapseNode(target, this);
        });
    }

    function toggleNode(target, node) {
        var hit = $('>span.tree-hit', node);
        if (hit.length == 0) return; // 叶子结点
        if (hit.hasClass('tree-expanded')) {
            collapseNode(target, node);
        } else {
            expandNode(target, node);
        }
    }

    function bindTreeEvents(target) {
        var opts = $.data(target, 'tree').options;
        var tree = $(target);
        $('.tree-node', tree).unbind('.tree').bind('dblclick.tree', function() {
            $($.data(target, 'tree.selected')).removeClass('tree-node-selected');
            $.data(target, 'tree.selected', this);

            $(this).addClass('tree-node-selected');
            if (opts.onDblClick) {
                opts.onDblClick.call(target, getNodeData(target, this));
            }
            return false;
        }).bind('click.tree', function() {
            $($.data(target, 'tree.selected')).removeClass('tree-node-selected');
            $.data(target, 'tree.selected', this);

            $(this).addClass('tree-node-selected');
            if (opts.onClick) {
                opts.onClick.call(target, getNodeData(target, this));
            }
            return false;
        }).bind('mouseenter.tree', function() {
            $(this).addClass('tree-node-hover');
            return false;
        }).bind('mouseleave.tree', function() {
            $(this).removeClass('tree-node-hover');
            return false;
        });
        $('.tree-hit', tree).unbind('.tree').bind('click.tree', function() {
            var node = $(this).parent();
            toggleNode(target, node);
            return false;
        }).bind('mouseenter.tree', function() {
            if ($(this).hasClass('tree-expanded')) {
                $(this).addClass('tree-expanded-hover');
            } else {
                $(this).addClass('tree-collapsed-hover');
            }
        }).bind('mouseleave.tree', function() {
            if ($(this).hasClass('tree-expanded')) {
                $(this).removeClass('tree-expanded-hover');
            } else {
                $(this).removeClass('tree-collapsed-hover');
            }
        });
        $('.tree-checkbox', tree).unbind('.tree').bind('click.tree', function() {
            if ($(this).hasClass('tree-checkbox0')) {
                $(this).removeClass('tree-checkbox0').addClass('tree-checkbox1');
            } else if ($(this).hasClass('tree-checkbox1')) {
                $(this).removeClass('tree-checkbox1').addClass('tree-checkbox0');
            } else if ($(this).hasClass('tree-checkbox2')) {
                $(this).removeClass('tree-checkbox2').addClass('tree-checkbox1');
            }
            setParentCheckbox(target, $(this).parent());
            setChildCheckbox(target, $(this).parent());
            return false;
        });
    }

    function setChildCheckbox(target, node) {
        var childck = node.next().find('.tree-checkbox');
        childck.removeClass('tree-checkbox0 tree-checkbox1 tree-checkbox2')
        if (node.find('.tree-checkbox').hasClass('tree-checkbox1')) {
            childck.addClass('tree-checkbox1');
        } else {
            childck.addClass('tree-checkbox0');
        }
    }

    function setParentCheckbox(target, node) {
        var pnode = getParentNode(target, node[0]);
        if (pnode) {
            var ck = $(pnode.target).find('.tree-checkbox');
            ck.removeClass('tree-checkbox0 tree-checkbox1 tree-checkbox2');
            if (isAllSelected(node)) {
                ck.addClass('tree-checkbox1');
            } else if (isAllNull(node)) {
                ck.addClass('tree-checkbox0');
            } else {
                ck.addClass('tree-checkbox2');
            }
            setParentCheckbox(target, $(pnode.target));
        }

        function isAllSelected(n) {
            var ck = n.find('.tree-checkbox');
            if (ck.hasClass('tree-checkbox0') || ck.hasClass('tree-checkbox2')) {
                return false;
            }
            var b = true;
            n.parent().siblings().each(function() {
                ck = $(this).find('.tree-checkbox');
                if (ck.hasClass('tree-checkbox0') || ck.hasClass('tree-checkbox2')) {
                    b = false;
                    return false;
                }
            });
            return b;
        }

        function isAllNull(n) {
            var ck = n.find('.tree-checkbox');
            if (ck.hasClass('tree-checkbox1') || ck.hasClass('tree-checkbox2')) {
                return false;
            }
            var b = true;
            n.parent().siblings().each(function() {
                ck = $(this).find('.tree-checkbox');
                if (ck.hasClass('tree-checkbox1') || ck.hasClass('tree-checkbox2')) {
                    b = false;
                    return false;
                }
            });
            return b;
        }
    }
    /**
     * 请求远程数据,加载到<url>
     */
    function request(target, ul, param) {
        var opts = $.data(target, 'tree').options;
        if (!opts.url) return;
        param = param || {};
        var folder = $(ul).prev().find('>span.tree-folder');
        folder.addClass('tree-loading');
        $.ajax({
            type: 'get',
            url: opts.url,
            data: param,
            dataType: 'json',
            success: function(data) {
                folder.removeClass('tree-loading');
                buildTree(target, ul, data);
                bindTreeEvents(target);
                if (opts.onLoadSuccess) {
                    opts.onLoadSuccess.apply(this, arguments);
                }
            },
            error: function() {
                folder.removeClass('tree-loading');
                if (opts.onLoadError) {
                    opts.onLoadError.apply(this, arguments);
                }
            }
        });
    }

    function getNodeData(target, nodeEle) {
        var treeData = $.data(target, 'tree.data');
        var node = $(nodeEle);
        return $.extend({}, treeData[node.attr('node-id')], {
            target: node[0],
            checked: node.find('.tree-checkbox').hasClass('tree-checkbox1')
        });
    }

    /**
     * 获取根结点
     * param: DOM object
     */
    function getRootNode(target) {
        var roots = getRootsNode(target);
        if (roots.length) {
            return roots[0];
        }
        return null;
    }

    function getRootsNode(target) {
        var treeData = $.data(target, 'tree.data');
        var roots = [];
        $(target).children("li").each(function() {
            var node = $(this).children("div.tree-node");
            roots.push(getNodeData(target, node[0]));
        });
        return roots;
    };

    /**
     * 获取父结点数据
     */
    function getParentNode(target, nodeEle) {
        var node = $(nodeEle).parent().parent().prev();
        if (node.length) {
            return getNodeData(target, node[0]);
        }
        return null;
    }

    /**
     * 获取子结点数据
     */
    function getChildrenNode(target, nodeEle) {
        var children = [];
        $(nodeEle).next().children().each(function() {
            var node = $(this).children('div.tree-node');
            children.push(getNodeData(target, node[0]));
        });
        return children;
    }

    function getCheckedNode(target, checkState) {
        var nodes = [];
        var cls = '.tree-checkbox1';
        if (checkState == 'unchecked') {
            cls = '.tree-checkbox0';
        } else if (checkState == 'indeterminate') {
            cls = '.tree-checkbox2';
        }
        $(target).find(cls).each(function() {
            var node = $(this).parent();
            nodes.push(getNodeData(target, node[0]));
        });
        return nodes;
    }
    /**
     * 获取选择结点数据,包含如下属性: id,text,attributes,target
     */
    function getSelectedNode(target) {
        var node = $(target).find('div.tree-node-selected');
        if (node.length) {
            return getNodeData(target, node[0]);
        } else {
            return null;
        }
    }

    /**
     * 查找结点,返回结点数据
     */
    function findNode(target, id) {
        var treeData = $.data(target, 'tree.data');
        if (id != undefined) {
            for (var k in treeData) {
                if (treeData[k].id == id) {
                    var node = $('div.tree-node[node-id="' + k + '"]', target)
                    return getNodeData(target, node[0]);
                }
            }
        }
        return null;
    };

    /**
     * 选择param结点
     * param: DOM object
     */
    function selectNode(target, param) {
        $('div.tree-node-selected', target).removeClass('tree-node-selected');
        $(param).addClass('tree-node-selected');
    }

    function checkNode(target, nodeEle, checked) {
        var node = $(nodeEle);
        setCheck(nodeEle, checked);
        node.next().find('div.tree-node').each(function() {
            setCheck(this, checked);
        });
        setParentCheckbox(target, node);

        function setCheck(nodeEle, checked) {
            var ck = $(nodeEle).find('.tree-checkbox');
            ck.removeClass('tree-checkbox0 tree-checkbox1 tree-checkbox2');
            if (checked) {
                ck.addClass('tree-checkbox1');
            } else {
                ck.addClass('tree-checkbox0');
            }
        }
    }

    /**
     * 检测param是不是叶子结点
     * param: DOM object
     */
    function isLeaf(target, nodeEle) {
        var node = $(nodeEle);
        var hit = $('>span.tree-hit', node);
        return hit.length == 0;
    }

    $.fn.tree = function(options, param) {
        if (typeof options == 'string') {
            return $.fn.tree.methods[options](this, param);
        }
        var options = options || {};
        return this.each(function() {
            var state = $.data(this, 'tree');
            var opts;
            var rtree;
            if (state) {
                $.extend(state.options, options);
            } else {
                opts = $.extend({}, $.fn.tree.defaults, $.fn.tree.parseOptions(this), options);
                $.data(this, 'tree', {
                    options: opts,
                    uid: 0
                });
                $.data(this, 'tree.data', {});
                if (opts.url) {
                    request(this, this);
                } else {
                    buildTree(this, this, opts.data);
                }
            }
            bindTreeEvents(this);
        });
    };

    $.fn.tree.methods = {
        options: function(jq) {
            return $.data(jq[0], 'tree').options;
        },
        reload: function(jq) {
            return jq.each(function() {
                $(this).empty();
                request(this, this);
            });
        },
        getRoot: function(jq) {
            return getRootNode(jq[0]);
        },
        getRoots: function(jq) {
            return getRootsNode(jq[0]);
        },
        getParent: function(jq, nodeEle) {
            return getParentNode(jq[0], nodeEle);
        },
        getChildren: function(jq, nodeEle) {
            return getChildrenNode(jq[0], nodeEle);
        },
        getChecked: function(jq, state) {
            return getCheckedNode(jq[0], state);
        },
        getSelected: function(jq) {
            return getSelectedNode(jq[0]);
        },
        isLeaf: function(jq, nodeEle) {
            return isLeaf(jq[0], nodeEle); // nodeEle是结点<div>
        },
        find: function(jq, id) {
            return findNode(jq[0], id);
        },
        select: function(jq, nodeEle) {
            return jq.each(function() {
                selectNode(this, nodeEle);
            });
        },
        check: function(jq, nodeEle) {
            return jq.each(function() {
                checkNode(this, nodeEle, true);
            });
        },
        uncheck: function(jq, nodeEle) {
            return jq.each(function() {
                checkNode(this, nodeEle, false);
            });
        },
        collapse: function(jq, nodeEle) {
            return jq.each(function() {
                collapseNode(this, $(nodeEle)); // nodeEle是结点<div>
            });
        },
        expand: function(jq, nodeEle) {
            return jq.each(function() {
                expandNode(this, $(nodeEle)); // nodeEle是结点<div>
            });
        },
        collapseAll: function(jq, nodeEle) {
            return jq.each(function() {
                collapseAllNode(this, nodeEle);
            });
        },
        expandAll: function(jq, nodeEle) {
            return jq.each(function() {
                expandAllNode(this, nodeEle);
            });
        },
        toggle: function(jq, nodeEle) {
            return jq.each(function() {
                toggleNode(this, $(nodeEle));
            });
        }
    };

    $.fn.tree.parseOptions = function(target) {
        return $.extend({}, $.parser.parseOptions(target, [
            'url', {
                checkbox: 'boolean',
                animate: 'boolean'
            }
        ]));
    };

    $.fn.tree.defaults = {
        url: null,
        animate: false,
        checkbox: false,
        data: [],
        onLoadSuccess: function() {},
        onLoadError: function() {},
        onClick: function(node) {}, // node: id,text,attributes,target
        onDblClick: function(node) {} // node: id,text,attributes,target
    };
})(jQuery);
